package sonnet.server;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.jws.WebMethod;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import sonnet.exceptions.NoRemoteMethodFoundException;
import sonnet.exceptions.RemoteInvocationException;
import sonnet.interfaces.RemoteObject;

/**
 * This servlet is used for entry point of remote invocations.
 * It should be declared in the web.xml of the web application.
 * Then its url is used as "target url" from clients
 *
 * @author Bozhidar Bozhanov
 */
public class RMServer extends HttpServlet {

	private static final long serialVersionUID = 1L;

	@SuppressWarnings({ "unchecked", "deprecation" })
	@Override
	public void service(HttpServletRequest request, HttpServletResponse response) {
		InputStream is = null;
		ObjectInputStream ois = null;
		ObjectOutputStream os = null;
		Exception resultException = null;

		try {
			is = request.getInputStream();
			ois = new ObjectInputStream(is);
			String className = (String) ois.readObject();
			String methodName = (String) ois.readObject();

			Serializable[] args = (Serializable[]) ois.readObject();
			Class[] argTypes = new Class[args.length];
			for (int i = 0; i < args.length; i ++) {
				argTypes[i] = args[i].getClass();
			}

			os = new ObjectOutputStream(response.getOutputStream());
			if (methodName.length() == 0) {
				RemoteObject ro = (RemoteObject) args[0];
				Serializable result = ro.execute();
				os.writeObject(result);
			} else {
				Class clazz = Class.forName(className);
				Method method = clazz.getDeclaredMethod(methodName, argTypes);
				if (method.getAnnotation(WebMethod.class) != null) {
					Serializable result = (Serializable) method.invoke(clazz.newInstance(), (Object[]) args);
					os.writeObject(result);
				} else {
					resultException = new NoRemoteMethodFoundException(
							"method " + methodName + " not annotated with WebMethod in class" + className);
				}
			}

		} catch (ClassNotFoundException ex) {
			resultException = new RemoteInvocationException(ex);
		} catch (NoSuchMethodException ex) {
			resultException = new NoRemoteMethodFoundException(ex.getMessage());
		} catch (IllegalAccessException ex) {
			resultException = new NoRemoteMethodFoundException(ex.getMessage());
		} catch (InvocationTargetException ex) {
			ex.printStackTrace();
			resultException = new NoRemoteMethodFoundException(ex.getMessage());
		} catch (InstantiationException ex){
			resultException = new RemoteInvocationException(ex);
		} catch (IOException ex) {
			resultException = new RemoteInvocationException(ex);
		} finally {
			try {
				os.writeObject(resultException);
				is.close();
				ois.close();
				os.close();
			} catch (Exception ioex) {
				ioex.printStackTrace();
				resultException.printStackTrace();
			}
		}
	}
}